package binding

import (
	"fmt"
	"net/http"
	"reflect"
)

const (
	ApplicationJSON                  = "application/json"
	ApplicationXML                   = "application/xml"
	ApplicationForm                  = "application/x-www-form-urlencoded"
	MultipartForm                    = "multipart/form-data"
)

const defaultMaxMemory = 32 << 20 // 32 MB

func BindRequest(req *http.Request, out interface{}) error {
	if out == nil {
		return nil
	}
	rv := reflect.Indirect(reflect.ValueOf(out))
	contentType := GetRequestContentType(req)
	switch contentType {
	case ApplicationJSON:
		return bindJSON(req, rv)
	case ApplicationForm:
		return bindForm(req, rv)
	case MultipartForm:
		return bindMultipartForm(req, rv)
	case ApplicationXML:
		fallthrough
	default:
		return fmt.Errorf("unsupported: %s", contentType)
	}
}

func bindJSON(req *http.Request, out interface{}) error {
	src, err := jsonSource(req)
	if err != nil {
		return err
	}
	return getBinder(req).Bind(src, out)
}

func bindForm(req *http.Request, out interface{}) error {
	if err := req.ParseForm(); err != nil {
		return err
	}
	return getBinder(req).Bind(formSource(req.PostForm), out)
}

func bindMultipartForm(req *http.Request, out interface{}) error {
	if err := req.ParseMultipartForm(defaultMaxMemory); err != nil {
		return err
	}
	return getBinder(req).Bind(multiSource(req.MultipartForm), out)
}

func getBinder(req *http.Request) binder {
	return binder{h: formSource(req.Header), q: formSource(req.URL.Query())}
}